# frozen_string_literal: true

class DiagramGraph
  attr_writer :diagram_type, :show_label, :alphabetize

  def initialize
    @diagram_type = ''
    @show_label   = false
    @alphabetize  = false
    @nodes = []
    @edges = []
  end

  def add_node(node)
    @nodes << node
  end

  def add_edge(edge)
    @edges << edge
  end

  # Generate DOT graph
  def to_dot
    dot_header +
      @nodes.map { |n| dot_node n[0], n[1], n[2], n[3] }.join +
      @edges.map { |e| dot_edge e[0], e[1], e[2], e[3] }.join +
      dot_footer
  end

  # Generate XMI diagram (not yet implemented)
  def to_xmi
    $stderr.print "Sorry. XMI output not yet implemented.\n\n"
    ''
  end

  private

  # Build DOT diagram header
  def dot_header
    result = "digraph #{@diagram_type.downcase}_diagram {\n" \
             "\tgraph[overlap=false, splines=true, bgcolor=\"none\"]\n"
    result += dot_label if @show_label
    result
  end

  # Build DOT diagram footer
  def dot_footer
    "}\n"
  end

  # Build diagram label
  def dot_label
    "\t_diagram_info [shape=\"plaintext\", " \
           "label=\"#{@diagram_type} diagram\\l" \
           "Date: #{Time.now.strftime '%b %d %Y - %H:%M'}\\l" +
      (if defined?(ActiveRecord::Migrator)
         'Migration version: ' \
              "#{Rails.logger.silence { ActiveRecord::Migrator.current_version }}\\l"
       else
         ''
       end) +
      "Generated by #{APP_HUMAN_NAME} #{APP_VERSION}\\l" + 'http://railroady.prestonlee.com' \
           "\\l\", fontsize=13]\n"
  end

  # Build a DOT graph node
  def dot_node(type, name, attributes = nil, custom_options = '')
    case type
    when 'model'
      options = "shape=Mrecord, label=\"{#{name}|"
      options += attributes.sort_by { |s| @alphabetize ? s : nil }.join('\l')
      options += '\l}"'
    when 'model-brief'
      options = ''
    when 'class'
      options = "shape=record, label=\"{#{name}|}\""
    when 'class-brief'
      options = 'shape=box'
    when 'controller'
      options = "shape=Mrecord, label=\"{#{name}|"
      public_methods    = attributes[:public].sort_by    { |s| @alphabetize ? s : nil }.join('\l')
      protected_methods = attributes[:protected].sort_by { |s| @alphabetize ? s : nil }.join('\l')
      private_methods   = attributes[:private].sort_by   { |s| @alphabetize ? s : nil }.join('\l')
      options += "#{public_methods}\\l|#{protected_methods}\\l|#{private_methods}\\l"
      options += '}"'
    when 'controller-brief'
      options = ''
    when 'module'
      options = "shape=box, style=dotted, label=\"#{name}\""
    when 'aasm'
      # Return subgraph format
      return "subgraph cluster_#{name.downcase.gsub(/[^a-z0-9\-_]+/i, '_')} {\n\tlabel = #{quote(name)}\n\t#{attributes.join("\n  ")}}"
    end
    options = [options, custom_options].compact.reject(&:empty?).join(', ')
    "\t#{quote(name)} [#{options}]\n"
  end

  # Build a DOT graph edge
  def dot_edge(type, from, to, name = '')
    options =  name != '' ? "label=\"#{name}\", " : ''
    edge_color = '"#%02X%02X%02X"' % [rand(255), rand(255), rand(255)]
    suffix = ", dir=both color=#{edge_color}"
    case type
    when 'one-one'
      options += "arrowtail=odot, arrowhead=dot#{suffix}"
    when 'one-many'
      options += "arrowtail=odot, arrowhead=crow#{suffix}"
    when 'many-many'
      options += "arrowtail=crow, arrowhead=crow#{suffix}"
    when 'belongs-to'
      # following http://guides.rubyonrails.org/association_basics.html#the-belongs-to-association
      options += "arrowtail=none, arrowhead=normal#{suffix}"
    when 'is-a'
      options += 'arrowhead="none", arrowtail="onormal"'
    when 'event'
      options += 'fontsize=10'
    end
    "\t#{quote(from)} -> #{quote(to)} [#{options}]\n"
  end

  # Quotes a class name
  def quote(name)
    "\"#{name}\""
  end
end
